home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ASME's Mechanical Engine…ing Toolkit 1997 December
/
ASME's Mechanical Engineering Toolkit 1997 December.iso
/
c_lang
/
super_c.lzh
/
S2LIB.ASM
< prev
next >
Wrap
Assembly Source File
|
1980-01-01
|
17KB
|
387 lines
; Serial Port Direct Interface Library
BUFSIZE = 2000 ; Size of buffer to use for receive
_BSS segment word public 'BSS' ; Global variables segment
_BSS ends
_TEXT segment byte public 'CODE' ; Code segment
_TEXT ends
DGROUP group _BSS ; Global variables are in this group
_BSS segment ; Declare variables
intSav dw 4 dup(?) ; Storage for saved interrupt vectors
bufIn dw 2 dup(?) ; Buffer input pointers
bufOut dw 2 dup (?) ; Buffer output pointers
buff0 db BUFSIZE dup(?) ; The COM1 circular buffer
buff1 db BUFSIZE dup(?) ; The COM2 buffer
_BSS ends ; End of variables
_TEXT segment ; Place the code in the code segment
assume CS:_TEXT, DS:DGROUP ; Assume CS points to code segment, and
; DS points to DRGOUP segment (where
; the variables are)
; _serInit(port,config)
;
; Function: Initialize and configure serial port port (0 for COM1, 1 for
; COM2). See the main text of the chapter for details on the format of the
; config parameter. Also set up incoming interrupts and buffering.
;
; Algorithm: Call the ROM BIOS to configure and initialize the port. Then
; save the existing interrupt vector and replace it with our own. Enable
; interrupts at the port, and return.
public _serInit ; Routine is available to other modules
port = 4 ; Offset from BP to parameter port
config = 6 ; Offset to parameter config
; These two tables are used to get the buffer pointers based on the
; port number. The first table contains pointers to the beginning of
; the buffers; the second contains pointers to the first byte immediately
; after the buffers.
bufAddr dw offset DGROUP:buff0, offset DGROUP:buff1
bufLim dw offset DGROUP:buff0+BUFSIZE, offset DGROUP:buff1+BUFSIZE
_serInit proc near ; NEAR type subroutine
push bp ; Save the BP register
mov bp,sp ; Set BP to SP; easier to get parameters
push di ; Save the DI register
push si ; Save the SI register
mov dx,[bp+port] ; DX = port number
mov al,[bp+config] ; AL = port configuration
mov ah,0 ; Call ROM BIOS port init function
int 14H
cli ; Disable interrupts
push es ; Save the ES register
xor ax,ax ; ES = 0 (to access low memory)
mov es,ax
mov si,[bp+port] ; SI = port # * 2
shl si,1
mov ax,cs:[si+bufAddr] ; Set the buffer to empty:
mov [si+bufIn],ax ; bufIn = bufOut = start of buffer
mov [si+bufOut],ax
mov bx,es:[si+400H] ; BX = base port address of serial port
mov si,1 ; SI = (1 - port #) * 4 (offset to vector)
sub si,[bp+port]
shl si,1
shl si,1
mov ax,es:[si+2CH] ; Save the old vector in intSav
mov [si+intSav],ax
mov ax,es:[si+2CH+2]
mov [si+intSav+2],ax
mov ax,OFFSET intServ ; Replace the vector with intServ
mov es:[si+2CH],ax
mov ax,_TEXT
mov es:[si+2CH+2],ax
mov dx,bx ; Read the serial port to clear it
in al,dx
add dx,4 ; Enable serial port receive interrupts
in al,dx
or al,8
out dx,al
sub dx,3
mov al,1
out dx,al
in al,21H ; Enable the 8259 interrupt controller
mov ah,0EFH
mov cl,[bp+port]
ror ah,cl
and al,ah
out 21H,al
sti ; Enable interrupts
pop es ; Restore ES
pop si ; Restore the SI register
pop di ; Restore the DI register
pop bp ; Restore the BP register
ret ; Return to calling program
_serInit endp ; End of subroutine
; intServ
;
; Function: Serial receive interrupt service routine. Handle an interrupt
; from a serial port and return.
;
; Algorithm: Save registers. Figure out which port the interrupt is from.
; Figure out what kind of interrupt it is, and process it accordingly.
; Acknowledge the interrupt at the 8259. Then restore registers and return
; from interrupt.
intServ proc near ; NEAR type subroutine (doesn't matter which
; type actually, since we do a RETI rather
; than a RET)
push ax ; Save the AX register
push bx ; Save BX
push cx ; Save CX
push dx ; Save DX
push si ; Save SI
push ds ; Save DS
push es ; Save ES
mov ax,DGROUP ; Set DS to DGROUP for access to variables
mov ds,ax
xor ax,ax ; Set ES to 0 for access to low memory
mov es,ax
;
; Find which port the interrupt is from:
;
xor si,si ; SI = 0 (assume COM1)
mov dx,es:400H ; DX = base port address for COM1
add dx,2 ; Get IIR address
in al,dx ; Get IIR contents
mov ah,al ; Make a copy of the input
and ah,1 ; Any interrupts here?
jz intC1 ; If there is an interrupt, go process it
mov dx,es:402H ; Otherwise, DX = base port address for COM2
add dx,2 ; Get IIR address for COM2
in al,dx ; Get IIR contents
mov ah,al ; Make a copy of it
and ah,1 ; Are there interrupts?
jnz notC2 ; If not, forget this interrupt
add si,2 ; If yes, increment SI for use with COM2
;
; Find out which type of interrupt it is, and handle it:
;
intC1: cmp al,0 ; Is it a modem status change?
jne notMSt ; If not, try the next interrupt type
add dx,4 ; If it is, read the modem status to clear it
in al,dx
jmp endInt ; And exit
notMSt: cmp al,2 ; Is it a transmit register empty interrupt?
jne notTRE ; If not, try other types
jmp endInt ; If it is, exit (we shouldn't get these)
notTRE: cmp al,4 ; Is it a receiver register full interrupt?
jne notRRF ; If not, try other types
sub dx,2 ; If yes, read in the received character
in al,dx
call putb ; Put it in the buffer
jmp endInt ; And exit
notRRF: cmp al,6 ; Is it a receive line status interrupt?
jne endInt ; If not, it's no known type -- ignore it
add dx,3 ; If yes, read the status to clear it
in al,dx
;
; Send end-of-interrupt to the 8259:
;
endInt: mov al,20H ; Send EOI
out 20H,al
;
; Restore registers and exit:
;
notC2: pop es ; Restore the ES register
pop ds ; Restore DS
pop si ; Restore SI
pop dx ; Restore DX
pop cx ; Restore CX
pop bx ; Restore BX
pop ax ; Restore AX
iret ; Return from interrupt
intServ endp ; End of routine
; serClose(port)
;
; Function: Close the port; restore it to the state it was in before serInit
; was called.
;
; Algorithm: Turn off interrupts and restore the interrupt vector to its
; original state.
public _serClose ; Routine is available to other modules
port = 4 ; Offset from BP to parameter port
_serClose proc near ; NEAR type subroutine
push bp ; Save the BP register
mov bp,sp ; Set BP to SP; easier to access parameters
push di ; Save the DI register
push si ; Save the SI register
push es ; Save the ES register
xor ax,ax ; Set ES to 0, for access to low memory
mov es,ax
cli ; Disbale interrupts
mov si,[bp+port] ; SI = 2*port #
shl si,1
mov bx,es:[si+400H] ; BX = I/O port base address
mov si,1 ; SI = 4*(1 - port #)
sub si,[bp+port]
shl si,1
shl si,1
mov ax,[si+intSav] ; Restore the interrupt from intSav
mov es:[si+2CH],ax
mov ax,[si+intSav+2]
mov es:[si+2CH+2],ax
mov dx,bx ; Turn off serial interrupts
add dx,4
in al,dx
and al,0F7H
out dx,al
sub dx,3
xor al,al
out dx,al
in al,21H ; Turn off 8259 controller ints
mov ah,10H
mov cl,[bp+port]
ror ah,cl
or al,ah
out 21H,al
mov al,20H
out 20H,al
sti ; Re-enble interrupts at the processor
pop es ; Restore the ES register
pop si ; Restore SI
pop di ; Restore DI
pop bp ; Restore BP
ret ; Return to calling program
_serClose endp ; End of subroutine
; _serSend(port,char)
;
; Function: Send the character char out over serial port port.
;
; Algorithm: Wait for the transmit holding register to be empty, and then
; output the character to be sent to that register.
public _serSend ; Routine is available to other modules
port = 4 ; Offset from BP to parameter port
char = 6 ; Offset to parameter char
_serSend proc near ; NEAR type subroutine
push bp ; Save the BP register
mov bp,sp ; Set BP to SP; easier to access parameters
push di ; Save the DI register
push si ; Save the SI register
push es ; Save ES
mov dx,[bp+port] ; DX = port #
xor ax,ax ; ES = 0 (for access to low memory)
mov es,ax
mov si,[bp+port] ; SI = 2*port #
shl si,1
mov dx,es:[si+400H] ; DX = I/O port base address
add dx,5 ; DX = address of line status register
sendWt: in al,dx ; Get line status
and al,20H ; Transmit holding register empty?
jz sendWt ; If not, keep asking...
sub dx,5 ; If yes, DX = address of trasmit holding reg.
mov al,[bp+char] ; Send character
out dx,al
pop es ; Restore the ES register
pop si ; Restore SI
pop di ; Restore DI
pop bp ; Restore BP
ret ; Return to caller
_serSend endp ; End of subroutine
; _serRecv(port)
;
; Function: If a character is available from serial port port, return it.
; Otherwise, return -1.
;
; Algorithm: Get the port number, turn off interrupts (so one doesn't sneak
; in and mess us up), and call getb to get a character out of the input
; buffer (if there's a character available).
public _serRecv ; Routine is available to other modules
port = 4 ; Offset from BP to parameter port
_serRecv proc near ; NEAR type subroutine
push bp ; Save the BP register
mov bp,sp ; Set BP to SP; easier to access parameters
push di ; Save the DI register
push si ; Save the SI register
mov si,[bp+port] ; SI = 2 * port #
shl si,1
cli ; Disable interrupts
call getb ; Get a character from the buffer
sti ; Enable interrupts
pop si ; Restore SI register
pop di ; Restore DI
pop bp ; Restore BP
ret ; Return to caller
_serRecv endp ; End of subroutine
; _serStat(port)
;
; Function: Return the status of the serial port specified.
;
; Algorithm: Call the ROM BIOS serial status function.
public _serStat ; Routine is available to other modules
port = 4 ; Offset from BP to the parameter port
_serStat proc near ; NEAR type subroutine
push bp ; Save the BP register
mov bp,sp ; Set BP to SP; easier to access parameters
push di ; Save the DI register
push si ; Save the SI register
mov dx,[bp+port] ; DX = port #
mov ah,3 ; AH = 3 (serial status function)
int 14H ; Call ROM BIOS serial port interrupt
pop si ; Restore the SI register
pop di ; Restore DI
pop bp ; Restore BP
ret ; Return to caller
_serStat endp ; End of subroutine
; putb(AL = byte, SI = offset)
;
; Function: Put a byte into a circular buffer; AL contains the byte,
; SI contains a word offset within the buffer pointers (0 for the COM1
; buffer, 2 for the COM2 buffer). If the character didn't fit in the
; buffer, putb returns with AH == -1; otherwise AH == 0.
;
; Algorithm: Get the bufIn pointer. Compute what the new bufIn will be.
; If it equals bufOut, the buffer is full. Otherwise, the character can
; be stored and bufIn updated.
putb proc near ; NEAR type subroutine
mov ah,-1 ; Assume the buffer'll be full
mov bx,[si+bufIn] ; BX = bufIn
mov cx,bx ; CX = (BX+1) modulo buffer
inc cx ; CX++
cmp cx,cs:[si+bufLim] ; Is it past the end of the buffer?
jne putb2 ; If not, continue
mov cx,cs:[si+bufAddr] ; If yes, reset it to the beginning
putb2: cmp cx,[si+bufOut] ; Is the buffer full?
je putb3 ; If it is, don't store this byte
mov [bx],al ; Otherwise, store the byte
xor ah,ah ; Set AH to indicate success
mov [si+bufIn],cx ; Update bufIn
putb3: ret ; Return
putb endp ; End of subroutine
; getb(SI = offset); returns AL = char
;
; Function: Get a byte into a circular buffer; SI contains a word
; offset within the buffer pointers (0 for the COM1 buffer, 2 for
; the COM2 buffer). On return, AX will contain a character from the
; buffer, or -1 if there were no bytes left in the buffer.
;
; Algorithm: Get the bufOut pointer. If it equals bufIn, the buffer
; is full. Otherwise, get a byte and increment bufOut.
getb proc near ; NEAR type subroutine
mov ax,-1 ; Assume we'll fail
mov bx,[si+bufOut] ; BX = bufOut
cmp bx,[si+bufIn] ; bufOut == bufIn?
je getb2 ; If yes, go exit
mov al,[bx] ; Otherwise, get byte from buffer
xor ah,ah ; Clear top byte of AX
inc bx ; Increment bufOut
cmp bx,cs:[si+bufLim] ; Past end of buffer?
jne getb3 ; If not, continue
mov bx,cs:[si+bufAddr] ; If yes, reset to the beginning
getb3: mov [si+bufOut],bx ; Update bufOut
getb2: ret ; Return
getb endp ; End of subroutine
_TEXT ends ; End of code segment
end ; End of assembly code